package cnpm02.j2ee.group3.logic.utils;

import java.io.Serializable;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.jboss.seam.Component;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.AutoCreate;
import org.jboss.seam.annotations.Create;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.international.LocaleSelector;

import cnpm02.j2ee.group3.logic.dao.generic.AbstractTranslationEntityDao;
import cnpm02.j2ee.group3.logic.dao.impl.CityTranslationDao;
import cnpm02.j2ee.group3.logic.dao.impl.GeneralRegulationDao;
import cnpm02.j2ee.group3.logic.dao.impl.LanguageTranslationDao;
import cnpm02.j2ee.group3.logic.dao.impl.NationalityTranslationDao;
import cnpm02.j2ee.group3.logic.dao.impl.SecretQuestionTranslationDao;
import cnpm02.j2ee.group3.logic.dao.impl.TicketTypeTranslationDao;
import cnpm02.j2ee.group3.model.AbstractEntity;
import cnpm02.j2ee.group3.model.AbstractTranslationEntity;
import cnpm02.j2ee.group3.model.City;
import cnpm02.j2ee.group3.model.GeneralRegulation;
import cnpm02.j2ee.group3.model.Language;
import cnpm02.j2ee.group3.model.Nationality;
import cnpm02.j2ee.group3.model.SecretQuestion;
import cnpm02.j2ee.group3.model.SupportedLanguage;
import cnpm02.j2ee.group3.model.TicketType;
import cnpm02.j2ee.group3.model.Translatable;

/**
 * This class holds all functions that is used elsewhere in atsm application
 * frequently.
 * 
 * @author BINH
 * @version 1.0
 * */
 @Name("atsmUtils")
 @Scope(ScopeType.SESSION)
 @AutoCreate
public class ATSMUtils implements Serializable {
	private static final long serialVersionUID = 1L;
	public static final String VI_LOCALE = "vi";
	public static final String EN_LOCALE = "en";
	public static final String DOT = ".";

	@In
	private LocaleSelector localeSelector;
	@In
	private GeneralRegulationDao generalRegulationDao;
	private MailService mailService = MailService.getService();

	private List<String> enMonths = Arrays.asList("January", "Feburary",
			"March", "April", "May", "June", "July", "August", "September",
			"October", "November", "December");

	private List<String> viMonths = Arrays.asList("Tháng 1", "Tháng 2",
			"Tháng 3", "Tháng 4", "Tháng 5", "Tháng 6", "Tháng 7", "Tháng 8",
			"Tháng 9", "Tháng 10", "Tháng 11", "Tháng 12");
	
	private List<String> paymentWays = Arrays.asList("ATM", "Credit",
			"Visa & Master");
	
	private GeneralRegulation lastestGeneralRegulation;
	
	private Map<Class<? extends AbstractEntity>, AbstractTranslationEntityDao<? extends AbstractTranslationEntity>> entityAndTranslationDao = new HashMap<Class<? extends AbstractEntity>, AbstractTranslationEntityDao<? extends AbstractTranslationEntity>>();

	@Create
	public void init() {
		entityAndTranslationDao.put(City.class,
				(CityTranslationDao) Component.getInstance(
						CityTranslationDao.COMPONENT_NAME, true));
		entityAndTranslationDao.put(Language.class,
				(LanguageTranslationDao) Component.getInstance(
						LanguageTranslationDao.COMPONENT_NAME, true));
		entityAndTranslationDao.put(Nationality.class,
				(NationalityTranslationDao) Component.getInstance(
						NationalityTranslationDao.COMPONENT_NAME, true));
		entityAndTranslationDao.put(SecretQuestion.class,
				(SecretQuestionTranslationDao) Component.getInstance(
						SecretQuestionTranslationDao.COMPONENT_NAME, true));
		entityAndTranslationDao.put(TicketType.class,
				(TicketTypeTranslationDao) Component.getInstance(
						TicketTypeTranslationDao.COMPONENT_NAME, true));
		
		lastestGeneralRegulation = generalRegulationDao.getLastestRegulation();
	}
	
	/**
	 * Get list of month base on current locale.
	 * @return {@link List} of month.
	 * */
	public List<String> getMonthsByCurrentLocale() {
		if (localeSelector.getLocale().toString().equalsIgnoreCase("vi")) {
			return viMonths;
		} else {
			return enMonths;
		}
	}
	
	/**
	 * Get list of month base on current locale.
	 * @param from from-month to December. 0 is indicated is January
	 * @return {@link List} of month.
	 * */
	public List<String> getMonthsByCurrentLocale(int from) {
		List<String> returns = new ArrayList<String>();
		if (localeSelector.getLocale().toString().equalsIgnoreCase("vi")) {
			for (int i = from; i < viMonths.size(); i++) {
				returns.add(viMonths.get(i));
			}
		} else {
			for (int i = from; i < viMonths.size(); i++) {
				returns.add(enMonths.get(i));
			}
		}
		
		return returns;
	}
	
	/**
	 * Get the list of year from the 'from' year to the 'to' year.
	 * @param from from year
	 * @param to to year
	 * @return {@link List} of year.
	 * */
	public List<Integer> getYears(int from, int to) {
		List<Integer> returnList = new ArrayList<Integer>();
		if (from > to) {
			return returnList;
		} else {
			for (int i = from; i <= to; i++) {
				returnList.add(i);
			}
		}
		return returnList;
	}

	/**
	 * Get list of year from the current year to the 'to' year.
	 * @param to year
	 * @return {@link List} of years.
	 * */
	public List<Integer> getYears(int to) {
		return getYears(Calendar.getInstance().get(Calendar.YEAR), to);
	}
	/**
	 * Get current language of the web application
	 * @return {@link SupportedLanguage}
	 * */
	public SupportedLanguage getCurrentLanguage() {
		if (VI_LOCALE.equals(localeSelector.getLocale().toString())) {
			return SupportedLanguage.VI;
		} else {
			return SupportedLanguage.EN;
		}
	}

	/**
	 * Send mail to a specific email address.
	 * @param emailAdd {@link String} the target email
	 * @param message {@link String} the will-be-sent message
	 * @return <code>true</code> if sent successfully, 
	 * 			<code>false</code> if failed.
	 * */
	public boolean sendMail(String receiver, String subject, String message) {
		return mailService.send(receiver, subject,  message);
	}
	
	/**
	 * Change the current locale to new locale
	 * @param newLanguage {@link String} new locale.
	 * */
	public void changeLanguage(String newLanguage) {
		localeSelector.setLanguage(newLanguage);
		localeSelector.select();
	}
	
	/**
	 * Get current locale name. Examples: "en", "de_DE", "_GB", "en_US_WIN", "de__POSIX", "fr__MAC".
	 * @return name of language {@link String}
	 * */
	public String getCurrentLanguageName() {
		return localeSelector.getLanguage();
	}
	
	/**
	 * Translate entity into current language
	 * @param entity {@link AbstractEntity}
	 * @return translation text {@link String}
	 * @throws throw {@link Exception} if <code>entity</code> is not translatable
	 * */
	public String translate(AbstractEntity entity) throws Exception {
		if(entity instanceof Translatable) {
			AbstractTranslationEntityDao<? extends AbstractTranslationEntity> dao = entityAndTranslationDao
					.get(entity.getClass());
			AbstractTranslationEntity translation = dao.select(entity, getCurrentLanguage());
			return translation.getTranslation();
			
		} else {
			throw new Exception("The object which need translating is not translatable!");
		}
	}
	
	/**
	 * Translate list of entity into current language
	 * @param entities list of entity which need to be translated
	 * @return {@link List} of translation text
	 * @throws Exception when one of entities is not translatable
	 * */
	public List<String> translate(List<? extends AbstractEntity> entities)
			throws Exception {
		List<String> results = new ArrayList<String>();
		for (AbstractEntity entity : entities) {
			if (entity instanceof Translatable) {
				AbstractTranslationEntityDao<? extends AbstractTranslationEntity> dao = entityAndTranslationDao
						.get(entity.getClass());
				AbstractTranslationEntity translation = dao.select(entity,
						getCurrentLanguage());
				results.add(translation.getTranslation());
			} else {
				throw new Exception(
						"The object among objects which need translating is not translatable!");
			}
		}
		Collections.sort(results);
		return results;
	}
	
	/**
	 * Get latest regulation
	 * @return {@link GeneralRegulation} latest regulation
	 * */
	public GeneralRegulation getLastestRegulation() {
		return lastestGeneralRegulation;
	}
	
	/**
	 * Return the string of a specific date
	 * @param date the date to get string of
	 * @param pattern the date pattern which to get, e.g dd.mm.yy, dd-mm-yyyy
	 * @return {@link String} of date
	 * */
	public String stringOf(Date date, String pattern) {
		DateFormat formater = new SimpleDateFormat(pattern);
		return formater.format(date);
	}
	
	/**
	 * Convert money to pretty format
	 * @param money
	 * @return {@link String}
	 * */
	public String formatMoney(long money) {
		String value = String.valueOf(money);
		StringBuilder result = new StringBuilder();
		int step = 3;
		result.append(value.substring(0, value.length() % step));
		for (int i = value.length() % step; i <= value.length();) {
			if (i + step <= value.length()) {
				if (i != 0) {
					result.append(".");
				}
				result.append(value.substring(i, i + step));
			}
			i += step;
		}

		return result.toString();
	}
	
	/**
	 * Convert a <code>set</code> to a list
	 * @param set the set need converting
	 * @return {@link List}
	 * */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public List<?> toList(Set<?> set) {
		return new ArrayList(set);
	}
	
	/**
	 * Get the 'name' of day in a week of the given day
	 * @param day 
	 * @return {@link String} return the 'name' of day such as: Monday, Tuesday, Thứ 2, Thứ 3...
	 * */
	public String getDayOfWeek(int day) {
		String result = "";
		switch (day) {
		case Calendar.SUNDAY:
			result = getCurrentLanguage() == SupportedLanguage.EN ? "Sunday"
					: "Chủ nhật";
			break;
		case Calendar.MONDAY:
			result = getCurrentLanguage() == SupportedLanguage.EN ? "Monday"
					: "Thứ Hai";
			break;
		case Calendar.TUESDAY:
			result = getCurrentLanguage() == SupportedLanguage.EN ? "Tuesday"
					: "Thứ Ba";
			break;
		case Calendar.WEDNESDAY:
			result = getCurrentLanguage() == SupportedLanguage.EN ? "Wednesday"
					: "Thứ Tư";
			break;
		case Calendar.THURSDAY:
			result = getCurrentLanguage() == SupportedLanguage.EN ? "Thursday"
					: "Thứ Năm";
			break;
		case Calendar.FRIDAY:
			result = getCurrentLanguage() == SupportedLanguage.EN ? "Friday"
					: "Thứ Sáu";
			break;
		case Calendar.SATURDAY:
			result = getCurrentLanguage() == SupportedLanguage.EN ? "Saturday"
					: "Thứ Bảy";
			break;
		}
		
		return result;
	}
	
	/**
	 * Get the 'name' of date with year exclusion
	 * @param date
	 * @return {@link String} return the 'name' of date such as: December 17, 17 Tháng 12
	 * */
	public String getDateExcludeYear(Date date) {
		StringBuilder result = new StringBuilder("");
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		result.append(cal.get(Calendar.DAY_OF_MONTH));
		result.append(" ");
		switch (cal.get(Calendar.MONTH)) {
		case Calendar.JANUARY:
			result = getCurrentLanguage() == SupportedLanguage.EN ? result
					.append("January") : result.append("Tháng 1");
			break;
		case Calendar.FEBRUARY:
			result = getCurrentLanguage() == SupportedLanguage.EN ? result
					.append("February") : result.append("Tháng 2");
			break;
		case Calendar.MARCH:
			result = getCurrentLanguage() == SupportedLanguage.EN ? result
					.append("March") : result.append("Tháng 3");
			break;
		case Calendar.APRIL:
			result = getCurrentLanguage() == SupportedLanguage.EN ? result
					.append("April") : result.append("Tháng 4");
			break;
		case Calendar.MAY:
			result = getCurrentLanguage() == SupportedLanguage.EN ? result
					.append("May") : result.append("Tháng 5");
			break;
		case Calendar.JUNE:
			result = getCurrentLanguage() == SupportedLanguage.EN ? result
					.append("June") : result.append("Tháng 6");
			break;
		case Calendar.JULY:
			result = getCurrentLanguage() == SupportedLanguage.EN ? result
					.append("July") : result.append("Tháng 7");
			break;
		case Calendar.AUGUST:
			result = getCurrentLanguage() == SupportedLanguage.EN ? result
					.append("August") : result.append("Tháng 8");
			break;
		case Calendar.SEPTEMBER:
			result = getCurrentLanguage() == SupportedLanguage.EN ? result
					.append("September") : result.append("Tháng 9");
			break;
		case Calendar.OCTOBER:
			result = getCurrentLanguage() == SupportedLanguage.EN ? result
					.append("October") : result.append("Tháng 10");
			break;
		case Calendar.NOVEMBER:
			result = getCurrentLanguage() == SupportedLanguage.EN ? result
					.append("November") : result.append("Tháng 11");
			break;
		case Calendar.DECEMBER:
			result = getCurrentLanguage() == SupportedLanguage.EN ? result
					.append("December") : result.append("Tháng 12");
			break;
		}
		
		return result.toString();
	}
	
	public List<String> getPaymentWays() {
		return paymentWays;
	}
	
	public boolean isEmpty(String str) {
		return str.isEmpty() || str == null;
	}
}
